-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: Add integrated web preview with element selection for AI context #5980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Implement WebPreviewProvider to manage webview lifecycle - Create WebPreviewView React component with device simulation - Add element selection mode with DOM inspection capabilities - Extract element context (HTML, CSS, XPath, position, styles) - Integrate with AI prompt system via webviewMessageHandler - Support responsive design testing with device presets - Add command registration and context menu integration - Include comprehensive tests for provider and component - Update documentation with usage instructions Fixes #5971
| </div> | ||
| <div className="controls"> | ||
| <VSCodeDropdown value={selectedDevice.name} onChange={handleDeviceChange}> | ||
| {DEVICES.map((device) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using the i18n translation function (t('...')) for UI labels such as "Select Element" and "Cancel Selection" to ensure language consistency across the application.
This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.
| }}> | ||
| <iframe | ||
| ref={iframeRef} | ||
| src={url} |
Check warning
Code scanning / CodeQL
Client-side URL redirect Medium
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 months ago
To fix the problem, we should validate the URL before setting it as the src attribute of the iframe. This can be done by ensuring the URL is either in a whitelist of allowed URLs, or at minimum, by checking that it is a safe origin (e.g., starts with http://localhost or other pre-approved hosts). We will implement a helper function, isSafeUrl, that checks whether a URL is permitted. In the message handler, before calling setUrl and setting the iframe's src, we will verify that the URL is safe; if not, we will ignore the message or set the URL to a default safe value.
We only need to edit webview-ui/src/components/webpreview/WebPreviewView.tsx, adding the helper function and updating the message handling logic. No changes are needed outside this file.
-
Copy modified lines R54-R68 -
Copy modified line R75
| @@ -51,37 +51,30 @@ | ||
| const iframeRef = useRef<HTMLIFrameElement>(null) | ||
| const containerRef = useRef<HTMLDivElement>(null) | ||
|
|
||
| // Helper to validate URLs before navigation | ||
| const isSafeUrl = (testUrl: string): boolean => { | ||
| try { | ||
| const urlObj = new URL(testUrl, window.location.origin) | ||
| // Only allow localhost, 127.0.0.1, or VSCode extension webview hosts | ||
| return ( | ||
| urlObj.hostname === "localhost" || | ||
| urlObj.hostname === "127.0.0.1" || | ||
| urlObj.protocol === "vscode-webview:" // VSCode webviews use custom protocols | ||
| ) | ||
| } catch (e) { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| // Handle messages from extension | ||
| useEffect(() => { | ||
| const handleMessage = (event: MessageEvent) => { | ||
| const message = event.data | ||
| switch (message.type) { | ||
| case "webPreviewConfig": | ||
| if (message.config?.defaultUrl) { | ||
| if (message.config?.defaultUrl && isSafeUrl(message.config.defaultUrl)) { | ||
| setUrl(message.config.defaultUrl) | ||
| } | ||
| break | ||
| case "webPreviewNavigate": | ||
| if (message.url) { | ||
| setUrl(message.url) | ||
| if (iframeRef.current) { | ||
| iframeRef.current.src = message.url | ||
| } | ||
| } | ||
| break | ||
| case "webPreviewSetDevice": { | ||
| const device = DEVICES.find((d) => d.name === message.device) | ||
| if (device) { | ||
| setSelectedDevice(device) | ||
| } | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| window.addEventListener("message", handleMessage) | ||
| return () => window.removeEventListener("message", handleMessage) | ||
| }, []) | ||
|
|
||
| // Notify extension when ready | ||
| useEffect(() => { |
| }}> | ||
| <iframe | ||
| ref={iframeRef} | ||
| src={url} |
Check warning
Code scanning / CodeQL
DOM text reinterpreted as HTML Medium
DOM text
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 months ago
To fix this problem, we should validate and sanitize the user input before using it as the src attribute of the iframe. Specifically, we should only allow URLs that use http: or https: schemes, and possibly further restrict to well-formed URLs. This can be done by parsing the input with the URL constructor, checking the scheme, and only updating the state if the URL is considered safe. If the input is invalid or unsafe, we should either reject the navigation or show an error. The changes should be made in the handler that updates the url state (in the onChange handler or in the handleNavigate function, if one exists).
-
Copy modified line R48 -
Copy modified line R54 -
Copy modified lines R56-R73
| @@ -45,13 +45,32 @@ | ||
|
|
||
| export const WebPreviewView: React.FC = () => { | ||
| const [url, setUrl] = useState("http://localhost:3000") | ||
| const [pendingUrl, setPendingUrl] = useState("http://localhost:3000") | ||
| const [selectedDevice, setSelectedDevice] = useState<Device>(DEVICES[0]) | ||
| const [isSelecting, setIsSelecting] = useState(false) | ||
| const [scale, setScale] = useState(1) | ||
| const iframeRef = useRef<HTMLIFrameElement>(null) | ||
| const containerRef = useRef<HTMLDivElement>(null) | ||
| const [urlError, setUrlError] = useState<string | null>(null) | ||
|
|
||
| // Handle messages from extension | ||
| // Utility to validate URLs: allow only http and https schemes | ||
| const isValidUrl = (input: string) => { | ||
| try { | ||
| const parsed = new URL(input, window.location.origin) | ||
| return parsed.protocol === "http:" || parsed.protocol === "https:" | ||
| } catch { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| const handleUrlInputChange = (e: any) => { | ||
| setPendingUrl(e.target.value) | ||
| setUrlError(null) | ||
| } | ||
|
|
||
| const handleNavigate = useCallback(() => { | ||
| if (isValidUrl(pendingUrl)) { | ||
| setUrl(pendingUrl) | ||
| useEffect(() => { | ||
| const handleMessage = (event: MessageEvent) => { | ||
| const message = event.data |
| }}> | ||
| <iframe | ||
| ref={iframeRef} | ||
| src={url} |
Check failure
Code scanning / CodeQL
Client-side cross-site scripting High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 months ago
To fix the vulnerability, any user-provided URL must be validated and sanitized before being used as the src attribute of the iframe. A good approach is to validate the URL against a whitelist of allowed origins or enforce a strict URL format (e.g., HTTPS URLs or localhost). If the URL is invalid, it should be rejected or replaced with a safe default.
The fix involves:
- Creating a utility function to validate and sanitize URLs.
- Applying this function before setting the
urlstate or using it in the iframe. - Updating the affected code paths to ensure untrusted data is sanitized.
-
Copy modified lines R48-R62 -
Copy modified line R76 -
Copy modified line R81 -
Copy modified line R83
| @@ -45,6 +45,21 @@ | ||
|
|
||
| export const WebPreviewView: React.FC = () => { | ||
| const [url, setUrl] = useState("http://localhost:3000") | ||
|
|
||
| // Helper function to validate and sanitize URLs | ||
| const sanitizeUrl = (inputUrl: string): string => { | ||
| try { | ||
| const parsedUrl = new URL(inputUrl); | ||
| // Allow only HTTP(S) URLs or localhost | ||
| if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") { | ||
| return inputUrl; | ||
| } | ||
| } catch (e) { | ||
| // Invalid URL, fall back to safe default | ||
| console.error("Invalid URL provided:", inputUrl); | ||
| } | ||
| return "http://localhost:3000"; // Safe fallback | ||
| }; | ||
| const [selectedDevice, setSelectedDevice] = useState<Device>(DEVICES[0]) | ||
| const [isSelecting, setIsSelecting] = useState(false) | ||
| const [scale, setScale] = useState(1) | ||
| @@ -58,14 +73,14 @@ | ||
| switch (message.type) { | ||
| case "webPreviewConfig": | ||
| if (message.config?.defaultUrl) { | ||
| setUrl(message.config.defaultUrl) | ||
| setUrl(sanitizeUrl(message.config.defaultUrl)) | ||
| } | ||
| break | ||
| case "webPreviewNavigate": | ||
| if (message.url) { | ||
| setUrl(message.url) | ||
| setUrl(sanitizeUrl(message.url)) | ||
| if (iframeRef.current) { | ||
| iframeRef.current.src = message.url | ||
| iframeRef.current.src = sanitizeUrl(message.url) | ||
| } | ||
| } | ||
| break |
|
This seems to have a lot of issues, it might be worth just starting over, closing for now |
This PR implements issue #5971 - adding an integrated web application preview feature with element selection capabilities for AI context.
Summary
This implementation adds a web preview panel to Roo Code that allows users to:
Key Features
1. Web Preview Panel
2. Element Selection Mode
3. AI Integration
4. Responsive Design Testing
Technical Implementation
Testing
Documentation
Updated README with usage instructions and feature overview.
Fixes #5971
Important
Adds a web preview feature to VS Code, enabling in-editor web application interaction and testing with AI context capture and responsive design simulation.
WebPreviewProviderinWebPreviewProvider.tsmanages webview lifecycle and communication.WebPreviewViewinWebPreviewView.tsxis the React component for the UI.WebPreviewView.cssfor layout and element highlighting.openWebPreviewadded toregisterCommands.tsandpackage.json.webPreviewNavigate,webPreviewElementSelectedinwebviewMessageHandler.ts.WebPreviewProviderinWebPreviewProvider.spec.ts.WebPreviewViewinWebPreviewView.spec.tsx.This description was created by
for fd0ed5a. You can customize this summary. It will automatically update as commits are pushed.